package com.mapbased.wfw.handler;

import java.io.*;
import java.nio.channels.*;
import javax.servlet.http.*;

import com.mapbased.wfw.*;

/**
 *
 * <p>Title: </p>
 *
 * <p>Description: </p>
 *
 * <p>Copyright: Copyright (c) 2004</p>
 *
 * <p>Company: mapbased.com</p>
 *
 * @author chy_hs(Baily)
 * @version 1.0
 */
public class MapScgHandler
    extends ScgHandler
{
    class ScgMeta
    {
        volatile long lastCheckTime= -1;
        volatile long lastUpdateTime;
        volatile java.lang.ref.SoftReference content;
        // volatile long length;
    }

    /**
     * conside using a soft-key map when a alot feature
     */
    java.util.Map metas=new java.util.HashMap();
    public Object keysBinding(java.util.Map m)
    {
        return this.metas.keySet();
    }

    private ScgMeta fetch(String key)
    {
        synchronized(metas)
        {
            Object o=metas.get(key);
            if(o != null)
            {
                return(ScgMeta)o;
            }
            ScgMeta meta=new ScgMeta();
            metas.put(key,meta);
            return meta;
        }
    }

    /**
     * if a thread is checking ,block all threads visiting the same meta
     * @param ctx RenderContext
     * @return byte[]
     * @throws IOException
     */
    protected byte[] filterRequest(RenderContext ctx)
        throws IOException
    {
        HttpServletResponse resp=ctx.getResponse();
        String key=this.getKey(ctx);
        ScgMeta meta=this.fetch(key);
        int s_interval=this.getCheckInterval(ctx);
        int i_interval=1000 * s_interval;
        long vback=meta.lastCheckTime;
        //if nend to check
        if(System.currentTimeMillis() - meta.lastCheckTime > i_interval)
        {
            synchronized(meta)
            {
                //if not eq, modified by another thread
                //just return
                if(meta.lastCheckTime == vback)
                {

                    File scgfile=new File(this.basefile,
                                          key.concat(".scg"));
                    long l=scgfile.lastModified();
                    /**
                     * maybe server is restarted
                     */
                    if(System.currentTimeMillis() - l < i_interval)
                    {
                        meta.lastCheckTime=l;
                        meta.lastUpdateTime=l;
                        //  meta.length=scgfile.length();
                    }
                    else //do real update
                    {
                        byte[] checkresult=null;
                        checkresult=this.checkToUpdate(ctx,scgfile);
                        meta.lastCheckTime=System.currentTimeMillis();

                        if(checkresult != null)
                        {
                            meta.lastUpdateTime=meta.lastCheckTime;
                            //   meta.length=checkresult.length;
                            meta.content=new java.lang.ref.SoftReference(
                                checkresult);
                            //  meta.content.
                            resp.setDateHeader(this.__Expires,
                                               System.currentTimeMillis() +
                                               i_interval);
                            resp.setHeader(this.__CacheControl,
                                           "max-age=" + s_interval);

                            resp.setDateHeader(this.__LastModified,
                                               meta.lastUpdateTime);

                            return checkresult;

                        }
                        else
                        {
                            meta.lastUpdateTime=l;
                            //  meta.length=scgfile.length();

                        }
                    }
                }
            }
        }

        long l=ctx.getRequest().getDateHeader(this.__IfModifiedSince);

        resp.setDateHeader(this.__Expires,
                           System.currentTimeMillis() + i_interval);
        resp.setHeader(this.__CacheControl,
                       "max-age=" + s_interval);

        if(l + 1000 >= meta.lastUpdateTime)
        {
            this.returnUnmodifyed(resp);
        }

        //  response.setHeader(this.__CacheControl,"max-age=" + s_interval);
        resp.setDateHeader(this.__LastModified,meta.lastUpdateTime);

        //   response.setHeader(this.__Age,String.valueOf( this.defaultCheckInterval/2));

        //  meta.content.get();
        byte[] result=null;
        if(meta.content != null)
        {
            result=(byte[])meta.content.get();
        }
        if(result == null)
        {

            FileInputStream fis=null;
            try
            {
                fis=new FileInputStream(new File(this.basefile,
                                                 key.concat(".scg")));
            }
            catch(FileNotFoundException ex)
            {
                meta.lastCheckTime=0l;
                meta.lastUpdateTime=0l;
                return this.filterRequest(ctx);

            }

            FileLock lock=fis.getChannel().lock(0,Long.MAX_VALUE,true);

            try
            {
                java.io.ByteArrayOutputStream bos=new java.io.
                    ByteArrayOutputStream();
                int b;
                while((b=fis.read()) != -1)
                {
                    bos.write(b);
                }
                result=bos.toByteArray();
                if(result.length == 0)
                {
                    log.warn("File content should not be zero!");
                    synchronized(metas)
                    {
                        this.metas.remove(key);
                    }
                }
                meta.content=new java.lang.ref.SoftReference(result);

                // result=new byte[(int)cl];
                //fis.read(result);
            }
            finally
            {
                lock.release();
                fis.close();
            }

        }

        return result;

    }
}
